﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common;

namespace AppDummy
{

    /// <summary>
    /// A dummy a module that 
    /// 1. sends ping messages to all active dummy ports
    /// </summary>

    [System.AddIn.AddIn("AppDummy")]
    public class AppDummy :  ModuleBase
    {

        bool showWindow = true;
        DummyWindow dummyWindow;

        //The dictionary for other dummy ports in the system
        Dictionary<View.VPort, View.VCapability> otherDummyPorts;

        public override void Start()
        {
            logger.Log("Started: {0}", ToString());
 
            //........... instantiate the list of other ports that we are interested in
            otherDummyPorts = new Dictionary<View.VPort, View.VCapability>();

            //..... get the list of current ports from the platform
            IList<View.VPort> allPortsList = GetAllPortsFromPlatform();

            if (allPortsList != null)
                ProcessAllPortsList(allPortsList);

            if (showWindow)
                ShowWindow();

            Work();
        }

        public void ShowWindow()
        {
            SafeThread thread = new SafeThread(
                                    delegate()
                                    {
                                        dummyWindow = new DummyWindow(this, logger, moduleInfo.FriendlyName());
                                        dummyWindow.ShowDialog();
                                    },
                                    "dummyWindow for " + ToString(),
                                    logger);
            thread.SetApartmentState(System.Threading.ApartmentState.STA);
            thread.Start();
        }

        public override void Stop()
        {
            if (dummyWindow != null) 
                 dummyWindow.Invoke(new Action(delegate() { dummyWindow.Close(); }));

            Finished();
        }

        /// <summary>
        /// Sit in a loop and spray the Pings to all active ports
        /// </summary>
        public void Work()
        {
            int counter = 0;
            while (true)
            {
                counter++;

                lock (this)
                {
                    if (dummyWindow != null)
                        dummyWindow.Invoke(new Action(delegate() { dummyWindow.OtherPortsLog(String.Format("{0} active dummy ports", otherDummyPorts.Count)); }));

                    List<View.VPort> ports = otherDummyPorts.Keys.ToList();
                    foreach (View.VPort port in ports)
                    {
                        // ..... if we do not have a capability, lets try to get one
                        if (otherDummyPorts[port] == null)
                        {
                            otherDummyPorts[port] = GetCapability(port, Globals.UserSystem);

                            //if we just got the capability, subscribe to the port
                            if (otherDummyPorts[port] != null)
                            {
                                port.Subscribe(RoleDummy.RoleName, RoleDummy.OpEchoSubName, ControlPort, otherDummyPorts[port], ControlPortCapability);
                            }
                        }

                        // ..... if we (now) have a capability, lets send an echo request
                        if (otherDummyPorts[port] != null)
                        {
                            SendEchoRequest(port, otherDummyPorts[port], counter);
                        }
                        // .... test to see if capability rejection is working
                        //else 
                        //    SendEchoRequest(port, new Capability(info.StrId, port, DateTime.MaxValue));

                    }
                }

                System.Threading.Thread.Sleep(1 * 10 * 1000);
            }
        }

        public void SendEchoRequest(View.VPort port, View.VCapability capability, int counter)
        {
            IList<View.VParamType> retVals;

            DateTime requestTime = DateTime.Now;

            IList<View.VParamType> parameters = new List<View.VParamType>();
            parameters.Add(new ParamType(ParamType.SimpleType.integer, "", counter, "echonum"));

            try
            {
                retVals = port.Invoke(RoleDummy.RoleName, RoleDummy.OpEchoName, parameters, ControlPort, capability, ControlPortCapability);
            }
            catch (Exception e)
            {
                logger.Log("Error while calling echo request: {0}", e.ToString());
                return;
            }

            double diffMs = (DateTime.Now - requestTime).TotalMilliseconds;

            string message = "";

            if (retVals[0].Maintype() != (int) ParamType.SimpleType.error)
            {
                int sentNum = counter;
                int rcvdNum = (int)retVals[0].Value();

                message = String.Format("echo success to {0} after {1} ms. sent = {2} rcvd = {3}", port.ToString(), diffMs.ToString(), sentNum.ToString(), rcvdNum.ToString());
            }
            else
            {
                message = String.Format("echo failure to {0} after {1} ms", port.ToString(), diffMs.ToString());
            }

            logger.Log("{0} {1}", this.ToString(), message);

            if (dummyWindow != null)
                dummyWindow.Invoke(new Action(delegate() { dummyWindow.ConsoleLog(message); }));

        }

        public override void AsyncReturn(string roleName, string opName, IList<View.VParamType> retVals, View.VPort senderPort)
        {
            string message;

            switch (opName.ToLower())
            {
                case "echosub":
                     int rcvdNum = (int) retVals[0].Value();

                     message = String.Format("async echo response from {0}. rcvd = {1}", senderPort.ToString(), rcvdNum.ToString());
                    break;
                default:
                    message = String.Format("Invalid async operation return {0} from {1}", opName.ToLower(), senderPort.ToString());
                    break;
            }

            logger.Log("{0} {1}", this.ToString(), message);

            lock (this)
            {
                if (dummyWindow != null)
                    dummyWindow.Invoke(new Action(delegate() { dummyWindow.ConsoleLog(message); }));
            }

        }

        public void ProcessAllPortsList(IList<View.VPort> portList)
        {
            foreach (View.VPort port in portList)
            {
                PortRegistered(port);
            }
        }

        /// <summary>
        ///  Called when a new port is registered with the platform
        /// </summary>
        public override void PortRegistered(View.VPort port)
        {

            logger.Log("{0} got registeration notification for {1}", ToString(), port.ToString());

            lock (this)
            {
                if (!otherDummyPorts.ContainsKey(port) && Role.ContainsRole(port, RoleDummy.RoleName) && !IsMyPort(port))
                {
                    //we'll get the capability the first time we send a message
                    otherDummyPorts[port] = null;
                    logger.Log("{0} added port {1}", this.ToString(), port.ToString());

                    if (dummyWindow != null)
                        dummyWindow.Invoke(new Action(delegate() { dummyWindow.ConsoleLog("added port " + port.ToString()); }));
                }
            }
        }

        public override void PortDeregistered(View.VPort port)
        {
            lock (this)
            {
                if (otherDummyPorts.ContainsKey(port))
                {
                    otherDummyPorts.Remove(port);
                    logger.Log("{0} deregistered port {1}", this.ToString(), port.GetInfo().ModuleFacingName());

                    if (dummyWindow != null)
                        dummyWindow.Invoke(new Action(delegate() { dummyWindow.ConsoleLog("removed port " + port.ToString()); }));
                }
            }
        }
    }
}